//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <wchar.h>
#include <dlfcn.h>
#include <limits.h>
#include <sched.h>

#include <auratypes.h>
#include <auraplus.h>
#include <aura_wchar.h>

#include "private.h"

#define IS_ENV_SEPARATOR(c) ((c) == ':')

//#define PERROR printf
//#define PTRACE printf


enum AURAFUNCTIONNAMES
{
    AuraModule_Load = 0,
    AuraModule_Unload,
    EnterModule,
    SetDebugFlag
};

char *g_auraFunctionNames[] =
{
    "AuraModule_Load",
    "AuraModule_Unload",
    "EnterModule",
    "SetDebugFlag",
};

UINT32 *g_auraFunctions;

static int BeingDebuged(int argc, char *argv[]);

void AnsiToUnic(wchar_t *pwszName, const char *pszName)
{
    while (*pszName) {
        *pwszName++ = (wchar_t)*pszName++;
    }
    *pwszName = 0;
}

Aura_Char *GetOneEnvPath(Aura_Char *des, Aura_Char *src)
{
    Aura_Char *s;
    Aura_Char *d;
    s = src;
    d = des;

    while('\0' != *s){
        *d = *s;
        if(IS_ENV_SEPARATOR(*s)){
            break;
        }
        s++;
        d++;
    }
    *d = '\0';
    //Stop behind the separator
    if('\0' != *s)s++;
    return s;
}

EXTERN_C INT32 GetModuleFullPath(Aura_Char *pszFullPath, Aura_Char *pszModuleName)
{
    Aura_Char pszPath[PATH_MAX];
    Aura_Char *pszEnvPath = NULL;
    Aura_Char *pszNextPathPoint = NULL;

    assert(pszFullPath && pszModuleName);

    //First get the moudle
    if (realpath(pszModuleName, pszPath) != NULL){
        strcpy(pszFullPath, pszPath);
        return (UInt32)pszFullPath;
    }

    //Try get the fullpath through "PATH"
    pszEnvPath = getenv("PATH");
    if(NULL == pszEnvPath){
        return 0;
    }

    pszNextPathPoint = pszEnvPath;
    do{
        pszNextPathPoint = GetOneEnvPath(pszPath, pszNextPathPoint);
        strcat(pszPath, "/");
        strcat(pszPath, pszModuleName);
        PTRACE("try pszPath:%s.\n", pszPath);
        if (realpath(pszPath, pszFullPath) != NULL){
            return (UInt32)pszFullPath;
        }
    }while('\0' != *pszNextPathPoint);

    return 0;
}

static int GetAuraFunctions(Aura_Handle hAuraModule)
{
    int i = 0;
    int count;
    char *error;

    count = sizeof(g_auraFunctionNames)/sizeof(char *);

    g_auraFunctions = (UInt32 *)malloc(count * sizeof(UInt32));
    if(NULL == g_auraFunctions){
        PERROR("Error: GetAuraFunctions failed! \
            file:%s. line:%d.\n", __FILE__, __LINE__);
        return 1;
    }

    for(i = 0; i < count; i++){
        g_auraFunctions[i] = (UInt32)dlsym((void *)hAuraModule, g_auraFunctionNames[i]);
        if ((void *)g_auraFunctions[i] == NULL)  {
            error = dlerror();
            PERROR("Error: GetAuraFunctions failed! func:%s, err:%s. \
            file:%s. line:%d. \n",
            g_auraFunctionNames[i], error, __FILE__, __LINE__);
            return 1;
        }
    }

    return 0;
}


typedef int (*ENTRY)(void);
typedef int (*EXPORTENTRY)(void);

Aura_Handle LoadAuraDll()
{
    Aura_Char szAuraFullPath[PATH_MAX];
    Aura_Handle hAuraModule = NULL;

    hAuraModule = (Aura_Handle)dlopen("libElAura.so", RTLD_NOW);

    if(NULL != hAuraModule){
        return hAuraModule;
    }

    if(!GetModuleFullPath(szAuraFullPath, "libElAura.so")){
        PERROR("Error: Load libElAura.dll failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return NULL;
    }

    hAuraModule = (Aura_Handle)dlopen(szAuraFullPath, RTLD_NOW);
    if (!hAuraModule) {
        PERROR("Error: load libElAura.dll failed! \
            %s, file:%s. line:%d. \
            %s\n", szAuraFullPath, __FILE__, __LINE__, dlerror());
        return NULL;
    }

    return hAuraModule;
}

void UnloadAuraDll(Aura_Handle hAuraModule)
{
    dlclose((void *)hAuraModule);
}

void __AnsiToUnic(
            /* [in] */ const char *src,
            /* [out] */ Aura_PWChar desc)
{
    while (*src) {
        *desc++ = (Aura_WChar)*src++;
    }
    *desc = 0;
}

typedef INT32 ModuleLoadFlags; //TODO
Aura_Handle LoadTargetModule(Aura_Handle hAuraModule, Aura_Char *pModuleName)
{
    typedef Aura_ECode (*AURA_MODULE_LOAD)(Aura_PWChar wsName,
                                    ModuleLoadFlags flags,
                                    Aura_Handle * phModule);

    ModuleLoadFlags flags = 0;
    AURA_MODULE_LOAD pLoadModule;
    Aura_Handle hTargetModule;
    Aura_WChar wszName[AURA_MAX_PATH];
    Aura_ECode ec;

    pLoadModule = (AURA_MODULE_LOAD)g_auraFunctions[AuraModule_Load];
    __AnsiToUnic(pModuleName, wszName);
    if(0 == wszName[0]){
        AURA_ERROR("failed to ansi->unicode.");
        return NULL;
    }

    ec = pLoadModule(wszName, flags, &hTargetModule);
    if(AURA_FAILED(ec)){
        AURA_ERROR("failed to load module.");
        return 0;
    }

    return hTargetModule;
}

void UnloadTargetModule(Aura_Handle hAuraModule, Aura_Handle hTargetModule)
{
    typedef Aura_ECode (*AURA_MODULE_UNLOAD)(Aura_Handle hModule);

    AURA_MODULE_UNLOAD pUnloadModule;

    pUnloadModule = (AURA_MODULE_UNLOAD)g_auraFunctions[AuraModule_Unload];

    pUnloadModule(hTargetModule);

}

INT32 EnterTargetModuleEntry(Aura_Handle hAuraModule, Aura_Handle hTargetModule, Aura_Char *pszArgument)
{
    typedef Aura_ECode (*ENTER_TARGET)(Aura_Handle hModule, Aura_Char *pszArg);
    ENTER_TARGET pEnter;

    pEnter = (ENTER_TARGET)g_auraFunctions[EnterModule];

    return pEnter(hTargetModule, pszArgument);
}

Aura_Handle g_hAuraModule;
Aura_Handle g_hTargetModule;
int g_fd;

void CleanDll(void)
{
    if(NULL != g_hAuraModule){
        if(NULL != g_hTargetModule){
            UnloadTargetModule(g_hAuraModule, g_hTargetModule);
        }
        UnloadAuraDll(g_hAuraModule);
    }
    if(g_fd){
        close(g_fd);
        g_fd = 0;
    }
}

void dumpargs(char **args)
{
    int i = 0;
    for(i = 0; NULL != args[i]; i++){
        PTRACE("argv[%d]:%s.\n", i, args[i]);
    }
}

int main(int argc, Aura_Char **argv)
{
    Aura_Handle hAuraModule;
    Aura_Handle hTargetModule;
    Aura_Char *szLoadModuleName = NULL;

    dumpargs(argv);

    if(0 != strstr(argv[0], "loader.exe")){
        if(NULL != argv[1]){
            szLoadModuleName = argv[1];
        } else {
            printf("Usage:./loader.exe FILENAME [ARGS]\n");
            return 2;
        }
    } else {
        szLoadModuleName = argv[0];
    }

    hAuraModule = LoadAuraDll();
    if(NULL == hAuraModule){
        return 1;
    }

    g_hAuraModule = hAuraModule;
    //Set the clean function
    atexit(CleanDll);

    if(GetAuraFunctions(hAuraModule)){
        return 1;
    }
    PTRACE("Loading Module Name:%s.\n", szLoadModuleName);

    BeingDebuged(argc, argv);
    hTargetModule = LoadTargetModule(hAuraModule, szLoadModuleName);
    if(NULL == hTargetModule){
        return 1;
    }
    g_hTargetModule = hTargetModule;

    EnterTargetModuleEntry(hAuraModule, hTargetModule, NULL/*Temporarily no use*/);
printf(" <%s>--------------7.\n", __FUNCTION__);
    return 0;
}

static int BeingDebuged(int argc, char *argv[])
{
    int i = 0;
    int fd;

    if(argc < 2){
        return 0;
    }

    for(i = 1; i < argc; i++){
        if(strstr(argv[i], "-D")){
            goto DEBUG;
        }
    }

    return 0;

DEBUG:

    typedef void (*SETDEBUGFLAG)(int fd);
    SETDEBUGFLAG pSetDebugFlag = (SETDEBUGFLAG)g_auraFunctions[SetDebugFlag];

    PTRACE("BeingDebuged fd:%s.\n", argv[i+1]);

    fd  = atoi(argv[i+1]);
    g_fd = fd;
    pSetDebugFlag(fd);

    return 0;
}

